﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Media;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Media;
using System.Windows.Threading;
using AvalonDock;
using Fluent;
using LSA.Framework;
using System.Linq;
using LSA.OnlineResources;
using Visuals.Graph;
using Visuals.Panes;
using Visuals.ViewModels;

namespace Visuals
{
    public delegate void StringParameterDelegate(string param);

    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : RibbonWindow
    {
        public static MainWindow Object = null;
        public LsaSpace lsaSpace;
        private bool wordsGrouped = false;
        public string[] fileNames;
        public Dictionary<string, byte> Entities;
        private bool showOnlyEntities = false;

        public bool CloseStart = false;

        public MainWindow()
        {
            DataContext = this;

            InitializeComponent();

            LoadStopWordsSettings();
            if (Object != null)
            {
                App.Current.Shutdown();
                return;
            }
            Object = this;
        }

        private void LoadStopWordsSettings()
        {
            stopWordsList.Items.Clear();
            foreach (string stopWord in Properties.Settings.Default.StopWords)
            {
                stopWordsList.Items.Add(stopWord);
            }
        }

        private void LoadWordsAndDocumentsList()
        {
            centralPane.Items.Clear();
            var dockable = new DockableContent { Title = "Добре дошли", Background = Brushes.White, Content = new WelcomeControl() };
            centralPane.Items.Add(dockable);


            documentsListControl.AllDocuments.Clear();
            documentsListControl.AllDocuments.AddRange(lsaSpace.AllDocuments);

            wordsListControl.AllWords.Clear();
            wordsListControl.AllWords.AddRange(lsaSpace.AllWords);

            webResultsControl.Results.Clear();
        }

        private void EnableButtons()
        {
            wordToWordButton.IsEnabled = true;
            wordsGroup.IsEnabled = true;
            wordsChartButton.IsEnabled = true;
            wordsGroupsPreview.IsEnabled = true;
            searchButton.IsEnabled = true;
            wordsClustersSpinner.Maximum = lsaSpace.WordsCount;
            wordsPerClusterSpinner.Maximum = lsaSpace.WordsCount;
            if (lsaSpace.WordsCount > 20)
            {
                wordsClustersSpinner.Value = 5;
                wordsPerClusterSpinner.Value = 20;
            }
        }

        private void SelectGeneralTab()
        {
            generalTab.IsOpen = true;
            generalTab.Visibility = Visibility.Visible;
            generalTab.IsSelected = true;
        }

        public void LoadCorpus()
        {
            loadCorpusButton_Click(this, new RoutedEventArgs());
        }

        public void OpenProject()
        {
            openButton_Click(this, new RoutedEventArgs());
        }

        public void BuildCorpus()
        {
            rebuildButton_Click(this, new RoutedEventArgs());
        }

        private void loadCorpusButton_Click(object sender, RoutedEventArgs e)
        {
            SelectGeneralTab();

            var dialog = new Microsoft.Win32.OpenFileDialog { Multiselect = true, Filter = "Text documents (.txt)|*.txt" };
            var folderResult = dialog.ShowDialog();
            if (folderResult == true)
            {
                try
                {
                    fileNames = dialog.FileNames;
                    numberOfDimensions.Maximum = fileNames.Length;
                    numberOfDimensions.Value = fileNames.Length;
                }
                catch (ArgumentException exception)
                {
                    MessageBox.Show(exception.Message, "Грешка при зареждане на корпус!", MessageBoxButton.OK, MessageBoxImage.Error);
                }
            }
        }

        private void saveButton_Click(object sender, RoutedEventArgs e)
        {
            SelectGeneralTab();

            var dialog = new Microsoft.Win32.SaveFileDialog { FileName = "Lsa Space", DefaultExt = ".lsa", Filter = "Lsa Space documents (.lsa)|*.lsa" };
            var folderResult = dialog.ShowDialog();
            if (folderResult == true)
            {
                Stream stream = File.Open(dialog.FileName, FileMode.Create);
                BinaryFormatter bFormatter = new BinaryFormatter();
                bFormatter.Serialize(stream, lsaSpace);
                stream.Close();

                SoundPlayer simpleSound = new SoundPlayer(@"Sounds\corpusSaved.wav");
                simpleSound.Play();
            }
        }

        public async void OpenCorpus(string fileName)
        {
            await TaskEx.Run(delegate()
                                 {
                                     Stream stream = null;
                                     try
                                     {
                                         stream = File.Open(fileName, FileMode.Open);
                                         BinaryFormatter bFormatter = new BinaryFormatter();
                                         lsaSpace = (LsaSpace)bFormatter.Deserialize(stream);
                                     }
                                     catch (SerializationException e)
                                     {
                                         MessageBox.Show("Проектът не бе валиден .lsa файл!");
                                     }
                                     catch (Exception) // TODO: not very cool
                                     {
                                         MessageBox.Show("Възникна проблем с прочитането на файла.");
                                     }
                                     finally
                                     {
                                         if(stream != null) stream.Close();
                                     }
                                 });

            if (lsaSpace == null)
                return;

            if (!File.Exists(lsaSpace.FileNames[0]))
            {
                MessageBox.Show(
                    "Файловете използвани в корпуса не бяха открити. Ще се опитаме да го възстановим, моля посочете един от файловете.");
                var dialogFile = new Microsoft.Win32.OpenFileDialog
                                     {Multiselect = false, Filter = "Text file (.txt)|*.txt"};
                var fileResult = dialogFile.ShowDialog();
                if (fileResult == true)
                {
                    string oldPath = lsaSpace.FileNames[0].Replace(lsaSpace.FileNames[0].Split('\\').Last(), "");
                    string newPath = dialogFile.FileName.Replace(dialogFile.FileName.Split('\\').Last(), "");

                    for (int i = 0; i < lsaSpace.FileNames.Length; i++)
                    {
                        lsaSpace.FileNames[i] = lsaSpace.FileNames[i].Replace(oldPath, newPath);
                        if (!File.Exists(lsaSpace.FileNames[0]))
                        {
                            MessageBox.Show(
                                "Възстановяването не бе успешно. Или сте избрали грешна директория, или някаи файлове липсват. Програмата ще се изключи.");
                            Application.Current.Shutdown();
                            return;
                        }
                    }
                }
            }

            LoadWordsAndDocumentsList();
            EnableButtons();

            if (lsaSpace.Entities.Count > 1)
            {
                showOnlyEntities = true;
            }

            SoundPlayer simpleSound = new SoundPlayer(@"Sounds\corpusLoaded.wav");
            simpleSound.Play();
        }

        private void openButton_Click(object sender, RoutedEventArgs e)
        {
            SelectGeneralTab();

            var dialog = new Microsoft.Win32.OpenFileDialog { Multiselect = false, Filter = "Lsa Space documents (.lsa)|*.lsa" };
            var folderResult = dialog.ShowDialog();
            if (folderResult == true)
            {
                OpenCorpus(dialog.FileName);
                CloseStart = true;
            }
        }

        private void aboutButton_Click(object sender, RoutedEventArgs e)
        {
            SelectGeneralTab();
            var about = new AboutWindow();
            about.ShowDialog();
        }

        private void wordsChartButton_Click(object sender, RoutedEventArgs e)
        {
            if (!IsCorpusLoaded()) return;
            var selectedItems = wordsListControl.GetSelectedWords();
            if (selectedItems.Count < 1)
            {
                MessageBox.Show("Трябва да изберете поне една дума.", "Грешка", MessageBoxButton.OK, MessageBoxImage.Error);
                return;
            }

            var words = selectedItems.Cast<WordInfo>().ToList();

            if (selectedItems.Count == 1)
            {
                var other = lsaSpace.GetKnnOfWord(((WordInfo)selectedItems[0]).Id,
                                                  (int)numberOfGraphicNodes.Value, showOnlyEntities);
                words.AddRange(other.Select(relationPair => relationPair.Word));
            }

            var charWindow = new CoordinateChartWindow(words) {Title = "Координатна система"};
            charWindow.Show();
        }

        private async void wordsGroup_Click(object sender, RoutedEventArgs e)
        {
            if (!IsCorpusLoaded()) return;

            await TaskEx.Run(delegate
                                 {
                                     Dispatcher.Invoke(DispatcherPriority.Normal, new Action(
                                                                                      () => lsaSpace.KMeansClusterWords(
                                                                                          (int)
                                                                                          wordsClustersSpinner.Value)));
                                 });

            wordsGrouped = true;
            MessageBox.Show("Групирането завърши успешно.");
        }

        private void wordsGroupsPreview_Click(object sender, RoutedEventArgs e)
        {
            if (!wordsGrouped)
            {
                MessageBox.Show("Трябва първо да групирате думи.");
                return;
            }

            GetStringForWordId deleg = lsaSpace.GetWordString;

            lsaSpace.GetAllWordsClusters((int)wordsPerClusterSpinner.Value);
            var previewWindow = new WordGroupsPreviewWindow(lsaSpace.GetAllWordsClusters((int)wordsPerClusterSpinner.Value), deleg);
            previewWindow.Show();
        }

        private async void rebuildButton_Click(object sender, RoutedEventArgs e)
        {
            var result = (lsaSpace == null) ? MessageBoxResult.Yes :
                MessageBox.Show("Сигурен ли си сте, че искате да стартирате изграждане на корпуса?",
                                "Проверка", MessageBoxButton.YesNo, MessageBoxImage.Question);
            if (result == MessageBoxResult.Yes)
            {
                fileNames = (fileNames == null && lsaSpace != null) ? lsaSpace.FileNames : fileNames;
                if (fileNames == null || (fileNames != null && fileNames.Length < 2))
                {
                    MessageBox.Show("Трябва да изберете поне два файла.");
                    return;
                }
                try
                {
                    int dimensions = (int)numberOfDimensions.Value;
                    bool tfidf = useTfIdf.IsChecked == true;
                    bool stem = shouldStem.IsChecked == true;
                    if (lsaSpace != null) Entities = lsaSpace.Entities;

                    await
                    TaskEx.Run(delegate
                                   {
                                       lsaSpace = new LsaSpace(fileNames, tfidf, stem,
                                                               Properties.Settings.Default.StopWords.Cast<string>().
                                                                   ToArray(), Entities, dimensions);
                                   });
                    LoadWordsAndDocumentsList();
                    EnableButtons();

                    if (lsaSpace.Entities.Count > 1)
                    {
                        showOnlyEntities = true;
                    }

                    SoundPlayer simpleSound = new SoundPlayer(@"Sounds\corpusBuilt.wav");
                    simpleSound.Play();
                }
                catch (ArgumentException exception)
                {
                    MessageBox.Show(exception.Message, "Грешка при зареждането на корпуса", MessageBoxButton.OK,
                                    MessageBoxImage.Error);
                }
            }
        }

        private void addStopWordButton_Click(object sender, RoutedEventArgs e)
        {
            if (!string.IsNullOrWhiteSpace(stopWordInput.Text) && !stopWordsList.Items.Contains(stopWordInput.Text))
            {
                stopWordsList.Items.Add(stopWordInput.Text);
                Properties.Settings.Default.StopWords.Add(stopWordInput.Text);
                Properties.Settings.Default.Save();
                stopWordInput.Text = "";
            }
        }

        private void removeStopWordButton_Click(object sender, RoutedEventArgs e)
        {
            if (stopWordsList.SelectedItems.Count == 1)
            {
                Properties.Settings.Default.StopWords.Remove(stopWordsList.SelectedItem.ToString());
                Properties.Settings.Default.Save();
                stopWordsList.Items.Remove(stopWordsList.SelectedItem);
            }
        }

        public void OpenNewSearchPreview(string word)
        {
            SearchPreview window = new SearchPreview(lsaSpace);

            if (word != null)
            {
                window.textBoxSearchBox.Text = word;
                window.InitializeSearch();
            }

            window.Show();
        }

        private void searchButton_Click(object sender, RoutedEventArgs e)
        {
            OpenNewSearchPreview(null);
        }

        private bool IsCorpusLoaded()
        {
            if (lsaSpace == null)
            {
                MessageBox.Show("Първо трябва да заредите корпус.", "Грешка", MessageBoxButton.OK, MessageBoxImage.Error);
                return false;
            }
            return true;
        }

        private async void WordsListClickCallback(WordInfo content)
        {
            if (!IsCorpusLoaded()) return;

            var newViewModel = new WordsGraphViewModel(content, lsaSpace.GetKnnOfWord(content.Id, (int)numberOfGraphicNodes.Value, showOnlyEntities));
            var graphControl = new WordsGraphControl(newViewModel);
            var dockable = new DockableContent { Title = content.Word };
            centralPane.Items.Add(dockable);
            dockable.Activate();
            dockable.Content = graphControl;

            ISearchEngine engine = new BingSearch(content.Word);
            List<SearchResult> results = new List<SearchResult>();

            await TaskEx.Run(delegate
                                 {
                                     lock (this)
                                     {
                                         try
                                         {
                                             results = engine.Search();
                                         }
                                         catch (System.Net.WebException)
                                         {
                                             MessageBox.Show("Проблем с връзката с интернет!");
                                         }
                                     }
                                 });

            webResultsControl.Results.Clear();
            webResultsControl.Results.AddRange(results);
        }

        private void AddWordToGraph(WordInfo word)
        {
            if (!IsCorpusLoaded()) return;

            foreach (DockableContent dockable in centralPane.Items)
            {
                if (dockable.State == DockableContentState.Document && dockable.IsActiveDocument)
                {
                    var control = (WordsGraphControl)dockable.Content;
                    var graph = control.ViewModel.Graph;

                    if (graph.Vertices.Any())
                    {
                        WordInfo fromWord =
                            graph.Vertices.OrderByDescending(x => lsaSpace.RelationBetweenWords(word.Id, x.Id)).First();

                        graph.AddVertex(word);
                        graph.AddEdge(new WordsGraphEdge(fromWord, word));
                    }
                    else
                    {
                        graph.AddVertex(word);
                    }
                }
            }
        }

        private void RibbonWindow_Closed(object sender, EventArgs e)
        {
            Application.Current.Shutdown();
        }

        private async void RibbonWindow_Loaded(object sender, RoutedEventArgs e)
        {
            StartWindow window = new StartWindow();
            await TaskEx.Run(() => Dispatcher.Invoke(DispatcherPriority.Normal, new Action(() => window.ShowDialog()))
            );
        }

        private void wordToWordButton_Click_1(object sender, RoutedEventArgs e)
        {
            if (!IsCorpusLoaded()) return;
            var selectedItems = wordsListControl.GetSelectedWords();
            if (selectedItems.Count != 2)
            {
                MessageBox.Show("Трябва да изберете точно две думи.", "Грешка", MessageBoxButton.OK, MessageBoxImage.Error);
                return;
            }

            var words = selectedItems.Cast<WordInfo>().ToList();
            var relation = lsaSpace.RelationBetweenWords(words[0].Id, words[1].Id);

            MessageBox.Show(string.Format("Връзката между {0} и {1} е {2}", words[0].Word, words[1].Word, relation));
        }
    }
}

